"""

.. module:: a2pdatastore_service

*Description*:

This module includes all functionality for mapping Spear Resources and the A2P
control Resource between Spaces and the DataStore. In general, application 
Resources and the A2P control Resource are maintained in both Spaces and the 
DataStore, but attachments are maintained in the DataStore only. This is performed
by monitoring the Resources in Spaces, as follows:

- application Resources:

  - on Spaces 'find' the requested Uri is returned if the related application 
    exists in the datastore.
  - on Spaces 'exists' TRUE is returned if the application exists in the datastore.
  - on Spaces 'get' applications are read from the datastore (if they exist)
    and written to Spaces at the requested Uri.
  - on Spaces 'put' and 'update' actions applications are retained in Spaces 
    AND written to the datastore.
  - on Spaces 'remove' applications are deleted from Spaces (by Spaces) and
    the datastore.
  
- attachment Resources:

  - on Spaces 'find' the requested Uri is returned if the related attachment 
    exists in the datastore.
  - on Spaces 'exists' TRUE is returned if the attachment exists in the datastore.
  - on Spaces 'get' attachments are read from the datastore (if they exist)
    and written to Spaces at the requested Uri.
  - on Spaces 'put' and 'update' attachments are written to the datastore and 
    then REMOVED from Spaces. This is so attachments, which over time will 
    become numerous, don't clutter Spaces. Access to attachments should be rare
    compared to applications.
  - on Spaces 'remove' attachments are deleted from Spaces (by Spaces) and
    the datastore.

- A2P control Resource:
- A2P user list:
- A2P RA list:

  - on Spaces 'find' the requested Uri is returned if the related control Resource 
    exists in the datastore.
  - on Spaces 'exists' TRUE is returned if the control Resource exists in the 
    datastore.
  - on Spaces 'get' the control Resource is read from the datastore (if it exists)
    and written to Spaces at the requested Uri.
  - on Spaces 'put' and 'update' actions the control Resource is retained in Spaces 
    AND written to the datastore.
  - on Spaces 'remove' the control Resource are deleted from Spaces (by Spaces) 
    and the datastore.

The following data is used to search the datastore for artifacts:

- applications:

  - the Spear reference number
  - ``'term:base/datastore/resource/type'`` == ``'term:eplanning/spear/application'``

- attachments: 

  - the document Id.
  - ``'term:base/datastore/resource/type'`` == ``'term:eplanning/spear/application-attachment'``

- A2P control Resource:

  - ``'term:base/datastore/resource/type'`` == ``'term:eplanning/spear/a2p-control'``

- A2P user list:

  - ``'term:base/datastore/resource/type'`` == ``'term:eplanning/spear/user-list'``

- A2P RA list:

  - ``'term:base/datastore/resource/type'`` == ``'term:eplanning/spear/ra-list'``

On startup, application Resources (but NOT attachments), the A2P control Resource,
A2P user list and A2P RA list Resources are copied from the datastore to Spaces.
 
*Constants*:

.. seealso:: 
"""

# Imports
#
import logging

from runtime.service import Service
from datastore.database import NotFoundException
from datastore.uri import Uri
from runtime.nodelogger import NodeLogger

from a2p_service import A2PService

# Constants
#

# Classes
#

class A2PDataStoreService(Service):
    """
    This class is responsible for mapping Spear Resources between Spaces and 
    the DataStore.
    
    **Constants**:
    
    :param spaces: runtime.spaces.Spaces - Reference to spaces
    :param ds: datastore.database.DataStore
    :param service_uri: String - the uri for this service. 
    """
    
    # Class constants
    #
    
    def __init__(self, spaces, ds, service_uri):
        # Constructor.
        #
        
        Service.__init__(self, spaces=spaces, datastore=ds, uri=service_uri, node_start=False)
        
        self.logger = NodeLogger.getLogger('eplanning.spear.a2pdatastore_service.A2PDataStoreService')
        self.logger.log(logging.DEBUG, "spaces (%s), ds (%s), service_uri (%s)" % (spaces, ds, service_uri))
        
        # Load spear resources from datastore into Spaces
        #
        try:
            rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/application'))])
        except NotFoundException:
            pass
        else:
            if (rs_list is not None):
                for rs in rs_list:
                    app_uri = A2PService.create_spear_application_uri(rs.get_metadata(Uri('spearReferenceNumber')))
                    self.logger.log(logging.DEBUG, "loading Spear application Resource (%s) to Spaces (%s)." % (rs.get_metadata(Uri('spearReferenceNumber')), app_uri))
                    self.spaces.put(app_uri, rs)
        
        # Load A2P control Resource to Spaces.
        #
        try:
            rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/a2p-control'))])
        except NotFoundException:
            pass
        else:
            if (rs_list is not None):
                for rs in rs_list:
                    self.logger.log(logging.DEBUG, "loading Spear control Resource to Spaces.")
                    self.spaces.put(A2PService.create_a2p_control_uri(), rs)
        
        # Load A2P user list to Spaces.
        #
        try:
            rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/user-list'))])
        except NotFoundException:
            pass
        else:
            if (rs_list is not None):
                for rs in rs_list:
                    self.logger.log(logging.DEBUG, "loading Spear user list Resource to Spaces.")
                    self.spaces.put(A2PService.create_spear_user_list_uri(), rs)
        
        # Load A2P control Resource to Spaces.
        #
        try:
            rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/ra-list'))])
        except NotFoundException:
            pass
        else:
            if (rs_list is not None):
                for rs in rs_list:
                    self.logger.log(logging.DEBUG, "loading Spear RA list Resource to Spaces.")
                    self.spaces.put(A2PService.create_spear_ra_list_uri(), rs)
        
    def cb_start(self, uri, action, rs):
        """
        This function is called when the service starts. 
        
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, probably 'put'.
        :param rs: Resource - This service's definition Resource.
        """

        self.logger.log(logging.DEBUG, "uri (%s), action (%s)" % (uri, action))
        
        # Watch for any change to a Spear application, attachment, A2P control, 
        # A2P user list and A2P RA list Resource.
        #
        self.spaces.set_watch(Uri('spear:app/'), self.cb_watch_spear_application_puts, ['put', 'update'])
        self.spaces.set_watch(Uri('spear:app/'), self.cb_watch_spear_application_removes, ['remove'])
        self.spaces.set_watch(Uri('spear:app-attachment/'), self.cb_watch_spear_attachment_puts, ['put', 'update'])
        self.spaces.set_watch(Uri('spear:app-attachment/'), self.cb_watch_spear_attachment_removes, ['remove'])
        self.spaces.set_watch(A2PService.create_a2p_control_uri(), self.cb_watch_a2p_control_puts, ['put', 'update'])
        self.spaces.set_watch(A2PService.create_a2p_control_uri(), self.cb_watch_a2p_control_removes, ['remove'])
        self.spaces.set_watch(A2PService.create_spear_user_list_uri(), self.cb_watch_a2p_user_list_puts, ['put', 'update'])
        self.spaces.set_watch(A2PService.create_spear_user_list_uri(), self.cb_watch_a2p_user_list_removes, ['remove'])
        self.spaces.set_watch(A2PService.create_spear_ra_list_uri(), self.cb_watch_a2p_ra_list_puts, ['put', 'update'])
        self.spaces.set_watch(A2PService.create_spear_ra_list_uri(), self.cb_watch_a2p_ra_list_removes, ['remove'])
        
        # Process any get(), exists() and find() requests for Spear applications,
        # attachments, A2P control, A2P user and A2P RA list Resources. These will 
        # be called if the Resource is NOT already in Spaces.
        #
        self.spaces.set_process(Uri('spear:app/'), self.cb_process_spear_application)
        self.spaces.set_process(Uri('spear:app-attachment/'), self.cb_process_spear_attachment)
        self.spaces.set_process(A2PService.create_a2p_control_uri(), self.cb_process_a2p_control)
        self.spaces.set_process(A2PService.create_spear_user_list_uri(), self.cb_process_a2p_user_list)
        self.spaces.set_process(A2PService.create_spear_ra_list_uri(), self.cb_process_a2p_ra_list)
        
    def cb_stop(self, uri, action, rs):
        """
        This function will stop any listeners still running.
        
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, probably 'put'.
        :param rs: Resource - This service's definition Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs.pretty_print()))
    
    def cb_watch_spear_application_puts(self, uri, action, rs):
        """
        This function is called from Spaces to process new or updates to Spear
        application Resources in Spaces. Here we write the Resource to the
        DataStore.
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, 'put' or 'update'.
        :param rs: Resource - The Spear application Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        spearReferenceNumber = rs.get_metadata(Uri('spearReferenceNumber'))
        try:
            rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/application')),
                                           (Uri('spearReferenceNumber'), spearReferenceNumber)])
        except NotFoundException:
            self.logger.log(logging.DEBUG, "Resource not found in datastore. Creating new record...")
            self.datastore.put(rs)
        else:
            if (rs_list is not None) and \
               (len(rs_list) > 0):
                self.logger.log(logging.DEBUG, "Resource (id=%s) found in datastore. Updating..." % (rs_list[0].get_id()))
                
                # copy the id so we update the datastore record rather than
                # creating a new record.
                #
                rs.set_id(rs_list[0].get_id())
                self.datastore.put(rs)
        
    def cb_watch_spear_application_removes(self, uri, action, rs):
        """
        This function is called from Spaces to process removes of Spear
        application Resources in Spaces. Here we delete the Resource from the
        DataStore.
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, 'delete'.
        :param rs: Resource - The Spear application Resource. 
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        self.datastore.delete(rs)
    
    def cb_process_spear_application(self, uri, action, rs):
        """
        This function is called from Spaces to process get()s, exists()s and
        find()s of Spear application Resources. 
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action.
        :param rs: Resource - The Spear application Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        uri_parts = uri.split()
        if (uri_parts is not None):
            spearReferenceNumber = uri_parts[-1]
            if (action == 'exists'):
                try:
                    rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/application')),
                                                   (Uri('spearReferenceNumber'), spearReferenceNumber)])
                except NotFoundException:
                    self.logger.log(logging.DEBUG, "Application Resource not found in DataStore")
                    return False
                else:
                    self.logger.log(logging.DEBUG, "Application Resource found in DataStore")
                    return True
                
            elif (action == 'get'):
                try:
                    rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/application')),
                                                   (Uri('spearReferenceNumber'), spearReferenceNumber)])
                except NotFoundException:
                    self.logger.log(logging.DEBUG, "Spear application Resource (%s) not found in DataStore" % (spearReferenceNumber))
                else:
                    self.logger.log(logging.DEBUG, "Spear application Resource found in DataStore. Putting to Spaces at Uri (%s)" % (uri))
                    
                    if (rs_list is not None) and \
                       (len(rs_list) > 0):
                        # make sure no callbacks are called, otherwise we'll end-up
                        # calling ourselves ad-infinitum.
                        #
                        self.spaces.put(uri, rs_list[0], spaces=True)
                        return rs_list[0]
            
            elif (action == 'find'):
                try:
                    rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/application')),
                                                   (Uri('spearReferenceNumber'), spearReferenceNumber)])
                except NotFoundException:
                    self.logger.log(logging.DEBUG, "Spear application Resource (%s) not found in DataStore" % (spearReferenceNumber))
                else:
                    self.logger.log(logging.DEBUG, "Spear application Resource found in DataStore. Returning Uri (%s)" % (uri))
                    
                    if (rs_list is not None) and \
                       (len(rs_list) > 0):
                        return [uri]
                
        return None
    
    def cb_watch_spear_attachment_removes(self, uri, action, rs):
        """
        This function is called from Spaces to process removes of Spear
        attachment Resources in Spaces. Here we delete the Resource from the
        DataStore.
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, 'delete'.
        :param rs: Resource - This Spear application attachment Resource. 
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        self.datastore.delete(rs)
    
    def cb_watch_spear_attachment_puts(self, uri, action, rs):
        """
        This function is called from Spaces to watch spaces put()s for Spear
        attachment Resources. This will be called after the Resource has been
        saved in Spaces. Here we write the Resource to the DataStore and remove
        it from Spaces so Spaces doesn't get clogged with attachments.
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, 'delete'.
        :param rs: Resource - The Spear attachment Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        if (action == 'put') or \
           (action == 'update'):
            documentId = rs.get_metadata(Uri('documentId'))
            try:
                rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/application-attachment')),
                                               (Uri('documentId'), documentId)])
            except NotFoundException:
                self.logger.log(logging.DEBUG, "Resource not found in datastore. Creating new record...")
                self.datastore.put(rs)
            else:
                if (rs_list is not None) and \
                   (len(rs_list) > 0):
                    self.logger.log(logging.DEBUG, "Resource (id=%s) found in datastore. Updating..." % (rs_list[0].get_id()))
                    
                    # copy the id so we update the datastore record rather than
                    # creating a new record.
                    #
                    rs.set_id(rs_list[0].get_id())
                    self.datastore.put(rs)
            
            # temporarily turn off our remove() handler so we don't delete the 
            # Resource from the datastore when we remove() the Resource from spaces.
            #
            self.spaces.del_watch(Uri('spear:app-attachment/'), self.cb_watch_spear_attachment_removes)
            
            self.spaces.remove(uri)
            
            # And re-establish the callback.
            #
            self.spaces.set_watch(Uri('spear:app-attachment/'), self.cb_watch_spear_attachment_removes, ['remove'])
        
        return None
    
    def cb_process_spear_attachment(self, uri, action, rs):
        """
        This function is called from Spaces to process get()s, exists()s and
        find()s of Spear attachment Resources. 
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action.
        :param rs: Resource - The Spear attachment Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        uri_parts = uri.split()
        if (uri_parts is not None):
            documentId = uri_parts[-1]
            
            if (action == 'exists'):
                try:
                    rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/application-attachment')),
                                                   (Uri('documentId'), documentId)])
                except NotFoundException:
                    self.logger.log(logging.DEBUG, "Attachment Resource not found in DataStore")
                    return False
                else:
                    self.logger.log(logging.DEBUG, "Attachment Resource found in DataStore")
                    return True
                
            elif (action == 'get'):
                try:
                    rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/application-attachment')),
                                                   (Uri('documentId'), documentId)])
                except NotFoundException:
                    self.logger.log(logging.DEBUG, "Spear application attachment Resource (%s) not found in DataStore" % (documentId))
                else:
                    self.logger.log(logging.DEBUG, "Spear application attachment Resource found in DataStore. Putting to Spaces at Uri (%s)" % (uri))
                    
                    if (rs_list is not None) and \
                       (len(rs_list) > 0):
                        # make sure no callbacks are called, otherwose we'll end-up
                        # calling ourselves ad-infinitum.
                        #
                        self.spaces.put(uri, rs_list[0], spaces=True)
                        return rs_list[0]
                    
            elif (action == 'find'):
                try:
                    rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/application-attachment')),
                                                   (Uri('documentId'), documentId)])
                except NotFoundException:
                    self.logger.log(logging.DEBUG, "Spear application attachment Resource (%s) not found in DataStore" % (documentId))
                else:
                    self.logger.log(logging.DEBUG, "Spear application attachment Resource (%s) found in DataStore. Returning Uri (%s)" % (documentId, uri))
                    
                    if (rs_list is not None) and \
                       (len(rs_list) > 0):
                        return [uri]
            
#            elif (action == 'remove'):
#                try:
#                    self.logger.log(logging.DEBUG, "****** remove, searching for docId (%s)" % (documentId))
#                    rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/application-attachment')),
#                                                   (Uri('documentId'), documentId)])
#                except NotFoundException:
#                    self.logger.log(logging.DEBUG, "Spear application attachment Resource (%s) not found in DataStore" % (documentId))
#                else:
#                    if (rs_list is not None) and \
#                       (len(rs_list) > 0):
#                        self.logger.log(logging.DEBUG, "****** remove, docId (%s) found" % (documentId))
#                        self.datastore.delete(rs_list[0])
#                        return rs_list[0]
                
        return None
    
    def cb_watch_a2p_control_puts(self, uri, action, rs):
        """
        This function is called from Spaces to process new or updates to the A2P
        control Resource in Spaces. Here we write the Resource to the DataStore.
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, 'put' or 'update'.
        :param rs: Resource - The A2P control Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        try:
            rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/a2p-control'))])
        except NotFoundException:
            self.logger.log(logging.DEBUG, "Resource not found in datastore. Creating new record...")
            self.datastore.put(rs)
        else:
            if (rs_list is not None) and \
               (len(rs_list) > 0):
                self.logger.log(logging.DEBUG, "Resource (id=%s) found in datastore. Updating..." % (rs_list[0].get_id()))
                
                # copy the id so we update the datastore record rather than
                # creating a new record.
                #
                rs.set_id(rs_list[0].get_id())
                self.datastore.put(rs)
        
    def cb_watch_a2p_control_removes(self, uri, action, rs):
        """
        This function is called from Spaces to process removes of the A2P control
        Resource in Spaces. Here we delete the Resource from the DataStore.
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, 'delete'.
        :param rs: Resource - The A2P control Resource. 
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        self.datastore.delete(rs)
    
    def cb_process_a2p_control(self, uri, action, rs):
        """
        This function is called from Spaces to process get()s, exists()s and
        find()s of the A2P control Resource. 
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action.
        :param rs: Resource - The A2P control Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        if (action == 'exists'):
            try:
                rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/a2p-control'))])
            except NotFoundException:
                self.logger.log(logging.DEBUG, "A2P control Resource not found in DataStore")
                return False
            else:
                self.logger.log(logging.DEBUG, "A2P control Resource found in DataStore")
                return True
            
        elif (action == 'get'):
            try:
                rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/a2p-control'))])
            except NotFoundException:
                self.logger.log(logging.DEBUG, "A2P control Resource not found in DataStore")
            else:
                self.logger.log(logging.DEBUG, "A2P control Resource found in DataStore. Putting to Spaces at Uri (%s)" % (uri))
                
                if (rs_list is not None) and \
                   (len(rs_list) > 0):
                    # make sure no callbacks are called, otherwise we'll end-up
                    # calling ourselves ad-infinitum.
                    #
                    self.spaces.put(uri, rs_list[0], spaces=True)
                    return rs_list[0]
        
        elif (action == 'find'):
            try:
                rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/a2p-control'))])
            except NotFoundException:
                self.logger.log(logging.DEBUG, "A2P control Resource not found in DataStore")
            else:
                self.logger.log(logging.DEBUG, "A2P control Resource found in DataStore. Returning Uri (%s)" % (uri))
                
                if (rs_list is not None) and \
                   (len(rs_list) > 0):
                    return [uri]
            
        return None
    
    def cb_watch_a2p_user_list_puts(self, uri, action, rs):
        """
        This function is called from Spaces to process new or updates to the A2P
        user list Resource in Spaces. Here we write the Resource to the DataStore.
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, 'put' or 'update'.
        :param rs: Resource - The A2P user list Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        try:
            rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/user-list'))])
        except NotFoundException:
            self.logger.log(logging.DEBUG, "Resource not found in datastore. Creating new record...")
            self.datastore.put(rs)
        else:
            if (rs_list is not None) and \
               (len(rs_list) > 0):
                self.logger.log(logging.DEBUG, "Resource (id=%s) found in datastore. Updating..." % (rs_list[0].get_id()))
                
                # copy the id so we update the datastore record rather than
                # creating a new record.
                #
                rs.set_id(rs_list[0].get_id())
                self.datastore.put(rs)
        
    def cb_watch_a2p_user_list_removes(self, uri, action, rs):
        """
        This function is called from Spaces to process removes of the A2P user list
        Resource in Spaces. Here we delete the Resource from the DataStore.
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, 'delete'.
        :param rs: Resource - The A2P user list Resource. 
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        self.datastore.delete(rs)
    
    def cb_process_a2p_user_list(self, uri, action, rs):
        """
        This function is called from Spaces to process get()s, exists()s and
        find()s of the A2P user list Resource. 
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action.
        :param rs: Resource - The A2P user list Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        if (action == 'exists'):
            try:
                rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/user-list'))])
            except NotFoundException:
                self.logger.log(logging.DEBUG, "A2P user list Resource not found in DataStore")
                return False
            else:
                self.logger.log(logging.DEBUG, "A2P user list Resource found in DataStore")
                return True
            
        elif (action == 'get'):
            try:
                rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/user-list'))])
            except NotFoundException:
                self.logger.log(logging.DEBUG, "A2P user list Resource not found in DataStore")
            else:
                self.logger.log(logging.DEBUG, "A2P user list Resource found in DataStore. Putting to Spaces at Uri (%s)" % (uri))
                
                if (rs_list is not None) and \
                   (len(rs_list) > 0):
                    # make sure no callbacks are called, otherwise we'll end-up
                    # calling ourselves ad-infinitum.
                    #
                    self.spaces.put(uri, rs_list[0], spaces=True)
                    return rs_list[0]
        
        elif (action == 'find'):
            try:
                rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/user-list'))])
            except NotFoundException:
                self.logger.log(logging.DEBUG, "A2P user list Resource not found in DataStore")
            else:
                self.logger.log(logging.DEBUG, "A2P user list Resource found in DataStore. Returning Uri (%s)" % (uri))
                
                if (rs_list is not None) and \
                   (len(rs_list) > 0):
                    return [uri]
            
        return None
    
    def cb_watch_a2p_ra_list_puts(self, uri, action, rs):
        """
        This function is called from Spaces to process new or updates to the A2P
        RA list Resource in Spaces. Here we write the Resource to the DataStore.
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, 'put' or 'update'.
        :param rs: Resource - The A2P RA list Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        try:
            rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/ra-list'))])
        except NotFoundException:
            self.logger.log(logging.DEBUG, "Resource not found in datastore. Creating new record...")
            self.datastore.put(rs)
        else:
            if (rs_list is not None) and \
               (len(rs_list) > 0):
                self.logger.log(logging.DEBUG, "Resource (id=%s) found in datastore. Updating..." % (rs_list[0].get_id()))
                
                # copy the id so we update the datastore record rather than
                # creating a new record.
                #
                rs.set_id(rs_list[0].get_id())
                self.datastore.put(rs)
        
    def cb_watch_a2p_ra_list_removes(self, uri, action, rs):
        """
        This function is called from Spaces to process removes of the A2P RA list
        Resource in Spaces. Here we delete the Resource from the DataStore.
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, 'delete'.
        :param rs: Resource - The A2P RA list Resource. 
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        self.datastore.delete(rs)
    
    def cb_process_a2p_ra_list(self, uri, action, rs):
        """
        This function is called from Spaces to process get()s, exists()s and
        find()s of the A2P RA list Resource. 
                
        :param uri: Uri - The service Uri.
        :param action: String - spaces action.
        :param rs: Resource - The A2P RA list Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        if (action == 'exists'):
            try:
                rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/ra-list'))])
            except NotFoundException:
                self.logger.log(logging.DEBUG, "A2P RA list Resource not found in DataStore")
                return False
            else:
                self.logger.log(logging.DEBUG, "A2P RA list Resource found in DataStore")
                return True
            
        elif (action == 'get'):
            try:
                rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/ra-list'))])
            except NotFoundException:
                self.logger.log(logging.DEBUG, "A2P RA list Resource not found in DataStore")
            else:
                self.logger.log(logging.DEBUG, "A2P RA list Resource found in DataStore. Putting to Spaces at Uri (%s)" % (uri))
                
                if (rs_list is not None) and \
                   (len(rs_list) > 0):
                    # make sure no callbacks are called, otherwise we'll end-up
                    # calling ourselves ad-infinitum.
                    #
                    self.spaces.put(uri, rs_list[0], spaces=True)
                    return rs_list[0]
        
        elif (action == 'find'):
            try:
                rs_list = self.datastore.find([(Uri('term:base/datastore/resource/type'), Uri('term:eplanning/spear/ra-list'))])
            except NotFoundException:
                self.logger.log(logging.DEBUG, "A2P RA list Resource not found in DataStore")
            else:
                self.logger.log(logging.DEBUG, "A2P RA list Resource found in DataStore. Returning Uri (%s)" % (uri))
                
                if (rs_list is not None) and \
                   (len(rs_list) > 0):
                    return [uri]
            
        return None